3

出现的问题

笔者前段时间开发一个新项目的某个功能模块,要读取老游戏中某个Mongo数据库(计作A库),连续读A库中的6个集合(相当于MySQL的6张表)取数据,在测试环境,单次操作时是10ms内,但是并发测试情况下,比如1s内100个并发时,读取A库6个集合的耗时能达到2s以上,甚至7,8s,且n个并发请求几乎同时完成,但是在我本地环境,并发下,平均一次操作也在几十ms到100多ms上下浮动,并发请求也比较正常

下面讲述一下整个处理过程

初步分析

虽然使用MongoDB的经验不多,但是在我的认知里,MongoDB的读取性能不可能如此低,不然如何投入生产环境?

那么,到底是哪里出了问题呢?

对比本地环境和测试环境使用方式,发现一个区别,我本地环境在Docker中跑的Mongo容器实例,为了方便,并没有设置用户名和密码,直接ip+端口号连接,而测试环境用了用户名密码,不管三七二十一,按照高中时候学的生物学中对照实验的思路,目前就只发现一个区别,先进行验证

本地连接Mongo的url
127.0.0.1:27017

本地环境配置用户名密码,得出结论:账号授权数据库切换耗时阻塞

先按照网上的教程,给Admin数据库建立一个管理员账号,拥有Mongo实例中读写任何一个db的权限

db.createUser({user:"root",pwd:"123456",roles:[{role:"userAdminAnyDatabase",db:"admin"}]})

使用账号密码后,无并发时,仍然是ms级别,但是发现,本地环境和测试环境一样,并发时,耗时达到数秒,且n个并发请求几乎同时完成,好像某个操作导致阻塞,然后这个阻塞操作完成后,没有阻塞只会,并发读取操作正常执行,可以确定是Mongo数据库的账号管理相关问题

为啥账号模块会导致这么大的缺陷?

通过神器Google搜索,看了n篇博客,发现一篇12年的博客

该文作者在生产环境使用Mongo数据库,有性能问题,其中一个关键点是,Mongo数据库,如果有用户名和密码,在每次建立数据库连接时,都会验证用户名密码,建议在生产环境不要使用用户名和密码,通过禁止外网访问,通过内网ip以及限制ip的方式,访问mongo数据库

由于线上老项目是lua写的,公司已经一年多未进行代码维护,且准备后面新项目上线后,直接下线老项目,笔者以及公司其他后端,都不懂lua,为了避免导致不可预料的问题,同时,在我看来,用户名和密码,对于数据库而言是最重要的安全模块,全球那么多公司在使用,就算账号验证有一定的性能损耗,但是也不可能有这么大的问题,应该还是我自己使用方面的问题,继续深入寻找原因

通过请教一位同事,公司另一个基础服务(计作B服务吧)在生产环境也用了MongoDB,且流量也很大,并没有问题,通过对比

我的方式:
user:123456@127.0.0.1:27017/admin
然后在代码中通过use,切换到baby库

B服务的方式:
针对baby数据库建立一个账号,由baby库授权
user:123456@127.0.0.1:27017/baby?authSource=baby&maxPoolSize=50

区别在于我是跳转到baby库,而正常环境下是使用该数据库下的用户

说明数据库授权耗时以及数据库切换也比较耗时,为啥会耗时,看到一篇博客讲到了Mongo的账号授权原理,知道了建立连接数据库授权时会导致库级锁(即连接a在使用时,连接b进行授权,也会导致baby库锁定,连接a被阻塞)

本地环境为数据库配置账户密码,直连,本地正常,测试环境依然延迟数秒

于是接着本地也适用B服务一样的方式,通过baby库下建立user账号,直连的方式连接,本地环境耗时正常,并发下读取6个集合,在100ms以内,属于正常性能,但是发到测试环境,并发下虽然耗时减少了,相对于之前的8s,现在依然有1s多的延迟

连接数据库方式一样,但是耗时差别很大,经过上一步知道了耗时是因为数据库锁机制导致的问题,后面在Google上搜Mongo锁机制,在知乎上看到了一个回答

mongodb 最初是全局锁,后来库锁,接着3.0进化到表锁了... 现在使用 WiredTiger 可以实现文档锁...

对比了测试环境和本地环境的版本,本地是最新的,已经是3.6版本,测试环境还是2.6版本,属于库锁

到此,可以知道问题所在了,MongoDB账号授权机制以及数据库锁机制

总结

  • 感兴趣的客观可以搜索MongoDB锁机制相关博客,网上文章很多,我就不在此啰嗦了
  • Mongo数据库,在生产环境,尽量通过限制内网ip才可以访问的方式保证安全,防止攻击
  • 其次,不要通过admin管理员用户连接数据库,该账号只是做为管理员使用,而不应该作为生产环境使用,MongoDB的思想是账号和数据库绑定,而不是实例,MySQL是一个MySQL实例对应账号,不针对某db
  • 尽量使用3.X的新版本MongoDB,数据库锁已经完善

以上只是我个人解决这个问题的全过程,博客写的不够思路清晰,过去了半个月,细节也记得不是很清楚了了,希望能给技术圈有所贡献,谢谢支持


tomorrowwu
444 声望27 粉丝

专注服务器开发,对区块链技术保持好奇心